// Copyright 2025 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license

package enum

import (
	"testing"

	"github.com/duke-git/lancet/v2/internal"
)

type Status int

const (
	Unknown Status = iota
	Active
	Inactive
)

func TestNewItemsFromPairs(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestNewItemsFromPairs")

	items := NewItemsFromPairs(
		Pair[Status]{Value: Active, Name: "Active"},
		Pair[Status]{Value: Inactive, Name: "Inactive"},
	)

	assert.Equal(2, len(items))
	assert.Equal(Active, items[0].Value())
	assert.Equal("Active", items[0].Name())
	assert.Equal(Inactive, items[1].Value())
	assert.Equal("Inactive", items[1].Name())
}

func TestItem_Valid(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestItem_Valid")

	item := NewItem(Active, "Active")
	assert.Equal(true, item.Valid())

	invalidItem := NewItem(Unknown, "")
	assert.Equal(false, invalidItem.Valid())
}

func TestItem_MarshalJSON(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestItem_MarshalJSON")

	item := NewItem(Active, "Active")
	data, err := item.MarshalJSON()
	assert.IsNil(err)
	assert.Equal("{\"name\":\"Active\",\"value\":1}", string(data))

	var unmarshaledItem Item[Status]
	err = unmarshaledItem.UnmarshalJSON(data)
	assert.IsNil(err)
	assert.Equal(item.Value(), unmarshaledItem.Value())
	assert.Equal(item.Name(), unmarshaledItem.Name())
}

func TestRegistry_AddAndGet(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_AddAndGet")

	registry := NewRegistry[Status]()
	item1 := NewItem(Active, "Active")
	item2 := NewItem(Inactive, "Inactive")

	registry.Add(item1, item2)

	assert.Equal(2, registry.Size())

	item, ok := registry.GetByValue(Active)
	assert.Equal(true, ok)
	assert.Equal("Active", item.Name())

	item, ok = registry.GetByName("Inactive")
	assert.Equal(true, ok)
	assert.Equal(Inactive, item.Value())
}

func TestRegistry_Remove(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_Remove")

	registry := NewRegistry[Status]()
	item1 := NewItem(Active, "Active")
	item2 := NewItem(Inactive, "Inactive")

	registry.Add(item1, item2)
	assert.Equal(2, registry.Size())

	removed := registry.Remove(Active)
	assert.Equal(true, removed)
	assert.Equal(1, registry.Size())

	_, ok := registry.GetByValue(Active)
	assert.Equal(false, ok)
}

func TestRegistry_Update(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_Update")

	registry := NewRegistry[Status]()
	item1 := NewItem(Active, "Active")
	registry.Add(item1)

	updated := registry.Update(Active, "Activated")
	assert.Equal(true, updated)

	item, ok := registry.GetByValue(Active)
	assert.Equal(true, ok)
	assert.Equal("Activated", item.Name())
}

func TestRegistry_Contains(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_Contains")

	registry := NewRegistry[Status]()
	item1 := NewItem(Active, "Active")
	registry.Add(item1)

	assert.Equal(true, registry.Contains(Active))
	assert.Equal(false, registry.Contains(Inactive))
}

func TestRegistry_Range(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_Range")

	registry := NewRegistry[Status]()
	item1 := NewItem(Active, "Active")
	item2 := NewItem(Inactive, "Inactive")
	registry.Add(item1, item2)

	var values []Status
	registry.Range(func(item *Item[Status]) bool {
		values = append(values, item.Value())
		return true
	})

	assert.Equal(2, len(values))
	assert.Equal(Active, values[0])
	assert.Equal(Inactive, values[1])
}

func TestRegistry_SortedItems(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_SortedItems")

	registry := NewRegistry[Status]()
	item1 := NewItem(Inactive, "Inactive")
	item2 := NewItem(Active, "Active")
	registry.Add(item1, item2)

	sortedItems := registry.SortedItems(func(i1, i2 *Item[Status]) bool {
		return i1.value < i2.value
	})

	assert.Equal(2, len(sortedItems))
	assert.Equal(Active, sortedItems[0].Value())
	assert.Equal(Inactive, sortedItems[1].Value())
}

func TestRegistry_Filter(t *testing.T) {
	t.Parallel()
	assert := internal.NewAssert(t, "TestRegistry_Filter")

	registry := NewRegistry[Status]()
	item1 := NewItem(Active, "Active")
	item2 := NewItem(Inactive, "Inactive")
	registry.Add(item1, item2)

	filteredItems := registry.Filter(func(item *Item[Status]) bool {
		return item.Value() == Active
	})

	assert.Equal(1, len(filteredItems))
	assert.Equal(Active, filteredItems[0].Value())
}
